package com.joy.redisstudy;

import java.util.*;

public class KanMenGou {
    /**
     * 通过Redis的看门购算法实现分布式锁
     * 逻辑：
     *  设置一个公共的锁，也就是每个任务，或者每个服务都是来拿这同一个锁
     *  开发一个切面去切需要加锁的就去，当方法进去切面的时候获取锁，没拿到锁，则等待
     *  拿到锁则开始止锁，并开启一个定时任务，在设置的时间之后去检查锁是否还被这个任务持有，如果持有，则给这个锁重新设置时间，即延时，没有持有则不管
     *  当方法执行完毕，主动去释放锁，让其他任务可以拿到锁
     * @author Joy
     */

    // 等待队列 使用LinkedList  有顺的，可先进先出
    private static List<String> waitingTaskList = new LinkedList<>();

    private static final String LOCK_KEY = "lock";

    private static Map<String, String> taskLockMap = new HashMap<>();

    // 默认失效时间  10秒
    private static final Integer times = 10;

    public static void main(String[] args) {

        System.out.println(tryLock("1"));
        System.out.println(tryLock("2"));
        System.out.println(tryLock("3"));
    }

    /**
     * 尝试获取锁
     * @param taskId
     * @return
     */
    public static Boolean tryLock(String taskId) {
        if (waitingTaskList.isEmpty()) { // 如果队列中没有等待任务，则直接拿到锁并返回
            // 记录是谁在持有锁
            taskLockMap.put(LOCK_KEY, taskId);
            checkLockTask(taskId);
            waitingTaskList.add(taskId);
            return true;
        } else {
            // 有锁在等等，则先把任务放到等待队列中
            waitingTaskList.add(taskId);
            return false;
        }
    }

    private static Integer cishu = 10;

    /**
     * 设置检查锁定时任务
     * @param taskId
     */
    public static void checkLockTask (String taskId) {
        // 开启一个定时任务，在设置的时间之后去检查锁是否还被这个任务持有，如果持有，则给这个锁重新设置时间，即延时，没有持有则不管
        Timer timer = new Timer();
        final int[] currentCishu = {0};
        TimerTask timerTask = new TimerTask() {
            @Override
            public void run() {
                System.out.println(taskId+"默认失效时间到，检查锁持有情况");
                // 持有锁的任务id
                String holdTaskId = taskLockMap.get(LOCK_KEY);
                currentCishu[0]++;
                System.out.println("已经延时"+currentCishu[0] + "次了");
                // 还持有着锁
                if (holdTaskId != null && holdTaskId.equals(taskId)) {
                    // 模拟释放锁
//                    taskLockMap.put(LOCK_KEY, null);
                    System.out.println(taskId+"持有锁，自动延时");
                } else {
                    System.out.println(taskId+"没有持有锁，不进行延时");
                    // 这个任务不需要再延时了，取消定时任务
//                    timer.cancel();
                }

                if (currentCishu[0] > cishu) {
                    System.out.println("超过最大延时次数，再无限得延迟");
                    timer.cancel();
                }
            }
        };
        timer.schedule(timerTask, 0,times * 1000);
    }
}
